<?php


namespace app\miniapp\controller\v1;


use app\common\cachemodel\Fortune;
use app\common\cachemodel\FortuneCache;
use app\common\cachemodel\Goods;
use app\common\cachemodel\MiniChannel;
use app\common\cachemodel\NatalGoods;
use app\common\cachemodel\NatalReport;
use app\common\cachemodel\Orders as OrdersCacheModel;
use app\common\cachemodel\OrdersGoods;
use app\common\cachemodel\OrdersLib;
use app\common\cachemodel\Recode;
use app\common\cachemodel\UserCoupon;
use app\common\lib\exception\ApiException;
use app\common\lib\PushQueue;
use app\common\lib\Util;
use app\common\model\Orders;
use app\miniapp\model\MiniVersion;
use think\Cache;
use think\Db;
use think\Exception;
use think\Log;
use think\response\Json;

class Order extends Auth
{

    protected $msg;

    /**
     * 通用下单检查接口
     */
    public function commonOrderCheck()
    {
        $rid = input('param.rid/s', '');
        $year = input('param.year/d', 0);
        $month = input('param.month/d', 0);
        $gid = input('param.gid/d', 0);
        $app_type = $this->headers['app-type'];

        if ('WEB' !== $app_type && !$this->miniInfo)
        { // 如果是小程序 并且未登陆
            return show(config('code.success'), '请求成功', ['src' => $this->getPreviewImg($gid)]);
        }
        $this->check_param($rid, $year, $month, $gid);
        if ($this->msg)
        {
//            Log::write('通用下单检查接口请求参数有误：' . $this->msg, 'notice', true);
            return show(config('code.error'), $this->msg);
        }

        //h5和抖音小程序，判断用户是否绑定了手机号；微信小程序先不判断，因为app里已经存在了微信登录，这里强制用户绑定手机号的话，有可能用户下不了单
        if ('WEB' !== $app_type && !$this->user)
        {
            return show(407, '为了更好的为您服务，请绑定手机号');
        }

        if (!in_array($gid, [20, 24, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58], true))
        {
            return show(config('code.error'), '商品下单错误，该商品不存在');
        }

        $adUnitId = '';
        if ($app_type === 'TT')
        {
            $channelId = (isset($this->headers['channel']) && !empty($this->headers['channel'])) ? $this->headers['channel'] : 31;
            $channelInfo = (new MiniChannel())->getInfoById($channelId, ['ad_unit_id']);
            if ($channelInfo)
            {
                $adUnitId = $channelInfo['ad_unit_id'];
            }
        }

        if (in_array($gid, [39, 40, 41, 42, 52, 54, 55, 56, 57, 58], true))
        {
            $goodType = in_array($gid, [39, 40, 41, 42, 52], true) ? 7 : 13;
            $NatalReportModel = new \app\common\cachemodel\NatalReport();
            $unStatus = 0;
            if ($this->user)
            {   // 已登陆获取档案信息 判断是否购买过该商品
                $recodeInfo = (new Recode())->getInfoById($rid);
                if (!$recodeInfo || $recodeInfo['uid'] != $this->user['id'])
                {
                    return show(config('code.error'), '请求异常,档案不存在');
                }
                // 获取购买商品状态
                // 获取解锁订单信息状态
                $unlockReport = $NatalReportModel->getNatalReport2($recodeInfo);
                // $unlockReport = $NatalReportModel->getNatalReport($this->user['id'], $rid);
                // if (isset($unlockReport[$gid]))
                // {
                //     return show(config('code.error'), '您已购买过此商品');
                // }
                $unStatus = isset($unlockReport[$gid]) ? $unlockReport[$gid]['status'] : 0;
            }
            //检查本命报告商品，开放状态status：2未开放；1：已开放；
            $now_time = time();
            //  获取本命预测商品信息
            $goodData = (new NatalGoods())->getGoods($goodType);
            foreach ($goodData as $item)
            {
                if ($gid !== (int)$item['id'])
                {
                    continue; // 不是当前商品则跳过
                }

                if (2 === (int)$item['status'])
                {   // 判断当前商品是否开发
                    return show(config('code.error'), '该商品未开放');
                }

                // TODO 学业报告临时活动
                if ((int)$item['id'] === 52 && $now_time >= 1595498400 && $now_time <= 1599321599)
                    // if ((int)$item['id'] === 52 && $now_time >= 1595433600 && $now_time <= 1599321599) // 测试代码
                {
                    $item['now_prize'] = "179.10";
                }

                $gData = [
                    'gid' => (int)($item['id'] ?? 0),
                    'name' => $item['name'] ?? '',
                    'price' => $item['now_prize'],
                    'desc' => $item['mini_desc'],
                    'src' => $this->getPreviewImg($gid),
                    'uen' => $this->user['uen'] ?? '',
                    'unlock_status' => $unStatus,
                    'order_type' => $goodType,
                    'ad_unit_id' => $adUnitId
                ];

                if ($this->headers['app-type'] === 'WEAPP')
                {
                    $gData['pay_mode_list'] = [
                        [
                            'id' => 2,
                            'name' => '微信支付',
                            'icon' => '/mini/astrolabe/order/wechat.png',
                            'is_selected' => 1,
                        ]
                    ];
                } else
                {//字节跳动 小程序，前端调用
                    $gData['pay_mode_list'] = [
                        [
                            'id' => 2,
                            'name' => '微信支付',
                            'icon' => '/mini/astrolabe/order/wechat.png',
                            'is_selected' => 1,
                        ], [
                            'id' => 1,
                            'name' => '支付宝支付',
                            'icon' => '/mini/astrolabe/order/alipay.png',
                            'is_selected' => 0,
                        ]
                    ];
                }

                $resCode = $this->user ? config('code.success') : 407;
                return show($resCode, '请求成功', $gData);
            }
            //循环结束，gid不再商品列表里
            Log::write('商品下单错误，该商品不存在', 'error', true);
            return show(config('code.error'), '商品下单错误，该商品不存在');
        } elseif (in_array($gid, [20, 53], true))
        {   // ----------月运---------------------------------------------------------------------------------------
            $goodType = $gid === 20 ? 3 : 12; // 3 月运 12 精简版月运
            //当前查看的时间
            $now_time = strtotime($year . '-' . $month);
            //解锁状态
            $unlock_status = 0;
            if ($this->user)
            {   // 获取登陆状态解锁月份
                $unlock_data = FortuneCache::getUnlockFortune($this->user['id'], $rid, $goodType);
            } else
            {
                $unlock_data = [
                    'note_time' => [],
                    'unlock_fortune' => [],
                    'computing_fortune' => [],
                    'completed_fortune' => []
                ];
            }
            switch (true)
            {
                case in_array($now_time, $unlock_data['computing_fortune']):
                    $unlock_status = 1;
                    break;
                case in_array($now_time, $unlock_data['completed_fortune']):
                    $unlock_status = 2;
                    break;
            }

            $goods = (new Goods())->GoodsTypeInfo($goodType);
            $price = 19.00;
            foreach ($goods as $good)
            {
                if ($good['id'] == $gid)
                {
                    $price = $good['now_prize'];
                }
            }
            $resData = [
                'gid' => $gid,
                'price' => $price,
                'unlock_status' => $unlock_status,
                'name' => '解锁月运',
                'uen' => $this->user['uen'] ?? '',
                'order_type' => $goodType,
                'ad_unit_id' => $adUnitId
            ];

            if ($this->headers['app-type'] === 'WEAPP')
            {
                $resData['pay_mode_list'] = [
                    [
                        'id' => 2,
                        'name' => '微信支付',
                        'icon' => '/mini/astrolabe/order/wechat.png',
                        'is_selected' => 1,
                    ]
                ];
            } else
            {
                $resData['pay_mode_list'] = [
                    [
                        'id' => 2,
                        'name' => '微信支付',
                        'icon' => '/mini/astrolabe/order/wechat.png',
                        'is_selected' => 1,
                    ], [
                        'id' => 1,
                        'name' => '支付宝支付',
                        'icon' => '/mini/astrolabe/order/alipay.png',
                        'is_selected' => 0,
                    ]
                ];
            }
            $resCode = $this->user ? config('code.success') : 407;
            return show($resCode, '请求成功', $resData);
        } elseif ($gid === 24)
        { // 年运
            $now_time = strtotime($year . '-01-01');
            //解锁状态
            $unlock_status = 0;
            if ($this->user)
            {   // 获取登陆状态解锁月份
                $unlock_data = FortuneCache::getUnlockFortune($this->user['id'], $rid, 4);
            } else
            {
                $unlock_data = [
                    'note_time' => [],
                    'unlock_fortune' => [],
                    'computing_fortune' => [],
                    'completed_fortune' => []
                ];
            }
            switch (true)
            {
                case in_array($now_time, $unlock_data['computing_fortune']):
                    $unlock_status = 1;
                    break;
                case in_array($now_time, $unlock_data['completed_fortune']):
                    $unlock_status = 2;
                    break;
            }

            $goods = (new Goods())->GoodsTypeInfo(4);
            $price = 19.00;
            foreach ($goods as $good)
            {
                if ($good['id'] == 24)
                {
                    $price = $good['now_prize'];
                }
            }
            $resData = [
                'gid' => 24,
                'price' => $price,
                'unlock_status' => $unlock_status,
                'name' => '解锁年运',
                'uen' => $this->user['uen'] ?? '',
                'order_type' => 4,
                'ad_unit_id' => $adUnitId
            ];

            if ($this->headers['app-type'] === 'WEAPP')
            {
                $resData['pay_mode_list'] = [
                    [
                        'id' => 2,
                        'name' => '微信支付',
                        'icon' => '/mini/astrolabe/order/wechat.png',
                        'is_selected' => 1,
                    ]
                ];
            } else
            {
                $resData['pay_mode_list'] = [
                    [
                        'id' => 2,
                        'name' => '微信支付',
                        'icon' => '/mini/astrolabe/order/wechat.png',
                        'is_selected' => 1,
                    ], [
                        'id' => 1,
                        'name' => '支付宝支付',
                        'icon' => '/mini/astrolabe/order/alipay.png',
                        'is_selected' => 0,
                    ]
                ];
            }
            $resCode = $this->user ? config('code.success') : 407;
            return show($resCode, '请求成功', $resData);
        }
    }


    /**
     * 获取订单状态
     */
    public function orderStatus()
    {
        $this->msg = '';
        $param = $this->request->only(['order_id']);
        if (!isset($param['order_id']) || !$param['order_id'])
        {
            return show(config('code.error'), '未知的订单');
        }
        $ordersCacheModel = new OrdersCacheModel();
        $orders = $ordersCacheModel->getInfoById($param['order_id']);
        if ($orders)
        {
//            0:等待支付;1:订单完成;2:订单关闭;3:退款成功;4:订单已完成;
            switch ($orders['status'])
            {
                case 0:
                    $this->msg = '等待支付';
                    break;
                case 4:
                case 1:
                    $this->msg = '订单完成';
                    break;
                case 3:
                    $this->msg = '退款成功';
                    break;
            }

            return show(config('code.success'), $this->msg, ['status' => (int)$orders['status'], 'order_type' => (int)$orders['order_type']]);
        }
        return show(config('code.error'), '订单不存在');
    }


    /**
     * 下单支付接口
     */
    public function orderPay()
    {
        $rid = input('rid/d', 0);
        $gid = input('gid/d', 0);  // 商品ID
        $tradeType = input('trade_type/d', 0);  //支付方式 1支付宝 2微信 3 广告激励解锁
        $orderType = input('order_type/d', 0);   // 商品类型 1=>生时矫正 2=>日运 3=>月运 4=>年运 5=>占星地图 6=>占星视频 7=>本命报告 8=>vip会员
        $time = input('time/s', '');  // 时间
        $callbackUrl = input('callback_url/s', '');  // 支付成功回跳地址
        $timestamp = input('timestamp/d', 0);
        $sign = input('sign/s', '');
        $os = $this->request->header('os', '');
        $appType = $this->request->header('app-type', '');

        //由于微信当前版本为1.0.2，运营已经签完投放协议，不能再提审1.0.3版本了，1.0.2版本没有传os参数，所以判断ua
        $ua = $this->headers['user-agent'];
        $num = substr_count($ua, 'iPhone');
        if ($num > 0)
        {
            $os = 'ios';
        }

        if ($os === 'ios' && $tradeType !== 3 && in_array($appType, ['TT', 'WEAPP'], true))
        {
            if ($appType === 'WEAPP')
            {
                $MiniVersion = new MiniVersion();
                $map = [
                    'version_id' => $this->headers['version'],
                    'mini_type' => 'WEAPP',
                    'app_type' => 1,
                    'status' => 1
                ];
                $version_info = [];
                try
                {
                    //判断微信小程序是否有版本控制数据
                    $version_info['weapp_btn_desc'] = $MiniVersion->getMiniAppBtnInfo($map, ['weapp_btn_desc']);
                }
                catch (\Exception $e)
                {

                }
                if (!empty($version_info['weapp_btn_desc']))
                {
                    return show(config('code.error'), 'ios不支持支付');
                }
            } else  //TT
            {
                $where = [
                    'version_id' => $this->headers['version'],
                    'mini_type' => 'TT',
                    'app_type' => '1',
                    'status' => 1
                ];
                $tt_mini_version_info = (new MiniVersion())->getMiniAppTtHintInfo($where);
                //如果存在版本控制的缓存，则关闭ios端购买
                if (!empty($tt_mini_version_info['tt_goods_desc']))
                {
                    return show(config('code.error'), 'ios不支持支付');
                }
            }
        }

        if (Cache::get($this->user['id'] . ':pay:miniapp'))
        {
            throw new ApiException('操作频繁,请稍候再试');
        }
        Cache::set($this->user['id'] . ':pay:miniapp', 1, 5);

        if (!in_array($tradeType, [1, 2, 3, 10], true))
        {
            return show(config('code.error'), '支付方式错误');
        }

        if (!$gid || !$rid)
        {
            return show(config('code.error'), '请求异常');
        }

        switch ($appType)
        {
            case 'WEAPP': // 微信小程序支付
                $tradeType = 13;
                $envType = 2;
                break;
            case 'TT': // 抖音小程序
                switch ($tradeType)
                {
                    case 1:
                        $tradeType = 10;  //  10=>抖音支付宝APP支付
                        break;
                    case 2:
                        $tradeType = 11;  // 11=>抖音微信H5支付
                        break;
                    case 3:
                        // 商品ID校验 判断是否为精简版商品ID
                        if (!in_array($gid, [53, 54, 55, 56, 57, 58], true))
                        {
                            return show(config('code.error'), '商品下单错误，该商品不存在');
                        }
                        // 签名校验
                        $sginData = [
                            'gid' => $gid,
                            'trade_type' => $tradeType,
                            'order_type' => $orderType,
                            'time' => $time,
                            'rid' => $rid,
                            'timestamp' => $timestamp
                        ];

                        $signRes = md5(md5(http_build_query($sginData)) . 'BvHa9WjkN1GceKg5');

                        if ($signRes !== $sign)
                        {
                            // return show(config('code.error'), '请求异常');
                        }
                        $tradeType = 14; // 14 广告免费解锁
                        break;
                    default:
                }
                $envType = 3;
                break;
            case 'ALIPAY': // 支付宝小程序
                $envType = 4;
                break;
            case 'SWAN': // 百度小程序
                $envType = 5;
                break;
            case 'QQ': // QQ小程序
                $envType = 6;
                break;
            case 'WEB': // H5
                $tradeType = $tradeType === 1 ? 7 : 8; // 8=>H5微信支付 7=>支付宝手机网页支付
                $envType = 7;
                break;
            default:
                $envType = 1;
        }
        $param = [
            'trade_type' => $tradeType,
            'order_type' => $orderType,
            'gids' => $gid,
            'param' => json_encode(['rid' => $rid, 'start_time' => $time]),
            'env_type' => $envType, //下单环境 1=>app,2=>微信小程序,3=>抖音小程序,4=>支付宝小程序,5=>百度小程序,6=>QQ小程序,7=>H5
            'channel' => Util::getChannelId(),
            'callbackUrl' => $callbackUrl ?? '',
            'openid' => $this->miniInfo['openid']
        ];
        $param = OrdersLib::pay_order_param_format($this->user['id'], $param);

        if ($tradeType === 14)
        {
            $res = $this->SaveFreeOrder($param);
            return show(config('code.success'), '解锁成功，正在计算，请您稍后查看...', ['status' => $res]);
        } elseif ($param['price'] >= 0.01)
        {
            //生成订单数据并下单
            $arr = $this->SaveOrder($param);
            return show(config('code.success'), '下单成功', $arr['data']);
        }
        return show(config('code.error'), '下单金额错误');
    }

    /**
     * 生成订单，并下单
     * @param $param ['order_type','trade_type','rid','cid','reid','gids','goods_info','price'，'start_time','out_trade_no','account','extend','pay_des','openid']
     * @return array|bool|Json
     * @throws ApiException
     * @throws \think\exception\PDOException
     */
    private function SaveOrder($param)
    {
        $now_time = time();
        // TODO 学业报告临时活动
        if ((int)$param['gids'][0] === 52 && $now_time >= 1595498400 && $now_time <= 1599321599)
            // if ((int)$param['gids'][0] === 52 && $now_time >= 1595433600 && $now_time <= 1599321599) // 测试代码
        {
            $param['price'] = 179.10;
        }
        $middle_res = true;
        $CouponModel = new UserCoupon();
        $orderModel = new OrdersCacheModel();
        Db::startTrans();
        try
        {
            //若是运势，则添加运势中间表数据
            if (in_array($param['order_type'], [2, 3, 4, 12], true))
            {
                if ($param['order_type'] === 12)
                {
                    $fortune_type = 3;
                } else
                {
                    $fortune_type = $param['order_type'];
                }
                $middle_res = $this->pay_fortune($param['rid'], $param['start_time'], $fortune_type);
            }
            if ($middle_res == false)
            {
                throw new ApiException('下单失败,请重新操作');
            }
            //生成订单数据
            if (in_array($param['trade_type'], [10, 11], true))
            {   // 如果是抖音 微信或者支付宝支付  10=>支付宝APP 11=>微信H5
                $tradeType = $param['trade_type'] === 10 ? 1 : 8; // 1=>支付宝APP支付 2=>微信H5支付
            } else
            {
                $tradeType = $param['trade_type'];
            }

            $insert = [
                'order_type' => $param['order_type'],
                'uid' => $this->user['id'],
                'gid' => $param['gids'][0],
                'order_id' => $param['out_trade_no'],
                'reid' => $param['reid'],
                'fid' => $middle_res,
                'trade_type' => $tradeType,
                'total_amount' => $param['price'],
                'create_time' => $this->request->time(),
                'status' => 0,
                'account' => $param['account'],
                'extend' => $param['extend'],
                'env_type' => $param['env_type'],
                'channel' => $param['channel'],
            ];
            // 生成订单到数据库
            try
            {
                //生成订单数据
                $oid = $orderModel->SaveData($insert);
                if ($oid === false)
                {
                    throw new ApiException('下单失败,请重新操作');
                }
                //添加中间表数据
                if ($param['rid'])
                {
                    $middle_res = $this->insertMiddle($param['rid'], $param['gids'], $oid);
                    if ($middle_res == false)
                    {
                        throw new ApiException('下单失败,请重新操作');
                    }
                }
            }
            catch (Exception $e)
            {
                Log::write('订单入库失败：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】', 'error');
            }
            //修改优惠券状态为已锁定
            if ($param['cid'])
            {
                $res = $CouponModel->use_return($this->user['id'], $param['cid'], ['status' => 4, 'oid' => $oid, 'update_time' => $this->request->time()]);
                if ($res === false)
                {
                    throw new ApiException($CouponModel->getError());
                }
            }
            //检测订单关闭入队
            PushQueue::CloseOrder($param['out_trade_no']);

            //更新订单详情缓存
            $orderModel->UpdateCache($param['out_trade_no'], 0);
            Db::commit();
        }
        catch (Exception $e)
        {
            Db::rollback();
            Log::write('入库失败信息：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】', 'error');
            throw new ApiException('下单失败,请重新操作');
        }

        //支付订单
        $arr = OrdersLib::unifyPayOrder([
            'oid' => $oid,
            'trade_type' => $param['trade_type'],
            'total_amount' => $param['price'],
            'pay_des' => $param['pay_des'],
            'order_id' => $param['out_trade_no'],
            'openid' => $param['openid'],
            'callback_url' => $param['callbackUrl'] ?? '',
            'order_type' => $param['order_type'],
            'uid' => (int)($this->user['id']),
        ], 1);
        $new_gid = [];
        foreach ($param['gids'] as $gid)
        {
            $new_gid[] = 'apple_pay_' . $gid;
        }
        $skuId = implode(',', $new_gid);

        if ($this->headers['app-type'] == 'ios')
        {
            $arr['data']['skuId'] = $skuId;
        }
        return $arr;
    }

    /**
     * 运势下单
     * @param int $rid
     * @param int $start_time
     * @param int $order_type
     * @return bool|int|string
     */
    private function pay_fortune($rid, $start_time, $order_type)
    {
        $arr = [
            'type' => $order_type - 1,
            'rids' => $rid,
            'time' => $start_time,
            'create_time' => $this->request->time(),
        ];
        try
        {
            $fid = (new Fortune())->insertGetId($arr); //每日运势id
        }
        catch (Exception $e)
        {
            return false;
        }

        return $fid;
    }

    /**
     * 添加中间表数据
     * @param $rid
     * @param $gids
     * @param $oid
     * @return bool
     */
    private function insertMiddle($rid, $gids, $oid)
    {
        $array = [];
        foreach ($gids as $k => $v)
        {
            $array[] = [
                'gid' => $v,
                'oid' => $oid,
                'rid' => $rid
            ];
        }
        //中间表
        try
        {
            (new OrdersGoods())->insertAll($array);
        }
        catch (Exception $e)
        {
            return false;
        }

        return true;
    }


    /**
     * 检测下单页请求参数
     * @param $rid
     * @param $year
     * @param $month
     * @return string
     */
    private function check_param($rid, $year, $month, $gid)
    {
        $this->msg = '';
        if (!$rid)
        {
            return $this->msg = '缺少档案';
        }
        if ($gid)
        {
            if ($gid <= 0)
            {
                return $this->msg = '商品id错误';
            }
        } else
        {
            if (!$year || !$month)
            {
                return $this->msg = '未知的时间';
            }
            if (!in_array($month, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true))
            {
                return $this->msg = '缺少月份';
            }
        }
    }

    private function getPreviewImg($gid)
    {
        //本命报告下单页预览图片地址
        switch ($gid)
        {
            case 39:
                $report_order_show_img = '/mini/astrolabe/order/feelings.png';
                break;
            case 40:
                $report_order_show_img = '/mini/astrolabe/order/dating.png';
                break;
            case 41:
                $report_order_show_img = '/mini/astrolabe/order/money.png';
                break;
            case 42:
                $report_order_show_img = '/mini/astrolabe/order/carrer.png';
                break;
            case 52:
                $report_order_show_img = '/mini/astrolabe/order/academic.png';
                break;
            case 54:
                $report_order_show_img = '/mini/astrolabe/order/sim_feelings.png';
                break;
            case 55:
                $report_order_show_img = '/mini/astrolabe/order/sim_dating.png';
                break;
            case 56:
                $report_order_show_img = '/mini/astrolabe/order/sim_money.png';
                break;
            case 57:
                $report_order_show_img = '/mini/astrolabe/order/sim_carrer.png';
                break;
            case 58:
                $report_order_show_img = '/mini/astrolabe/order/sim_academic.png';
                break;
            default:
                $report_order_show_img = '';
        }

        return $report_order_show_img;
    }

    /**
     * 生成订单数据并入队
     * @param $param [cid,rid,start_time,order_type,gids,out_trade_no,reid,extend,goods_info]
     * @return array
     * @throws ApiException
     * @throws Exception
     */
    private function SaveFreeOrder($param)
    {
        $middle_res = true;
        $CouponModel = new UserCoupon();
        $now_time = $this->request->time();
        $gid = current($param['gids']);
        //订单状态
        switch (true)
        {
            case  in_array($param['order_type'], [1, 3, 4, 12, 13]) || ($param['order_type'] == 5 && in_array($gid, [18, 19]))://需要计算的订单
                $status = 1;
                break;
            case in_array($param['order_type'], [2, 6, 8]) || ($param['order_type'] == 5 && !in_array($gid, [18, 19]))://无需计算的订单
                $status = 4;
                break;
            default:
                $status = 1;
                break;
        }
        $num = (int)(substr($param['out_trade_no'], 1, 1));
        Db::startTrans();
        try
        {
            //若是运势，则添加运势中间表数据
            if (in_array($param['order_type'], [2, 3, 4, 12], true))
            {
                if ($param['order_type'] === 12)
                {
                    $fortune_type = 3;
                } else
                {
                    $fortune_type = $param['order_type'];
                }
                $middle_res = $this->pay_fortune($param['rid'], $param['start_time'], $fortune_type);
            }
            if ($middle_res == false)
            {
                throw new ApiException('下单失败,请重新操作');
            }
            //生成订单数据
            $insert = [
                'order_type' => $param['order_type'],
                'uid' => $this->user['id'],
                'gid' => $gid,
                'order_id' => $param['out_trade_no'],
                'reid' => $param['reid'],
                'fid' => $middle_res,
                'trade_type' => $param['trade_type'],
                'total_amount' => 0,
                'create_time' => $now_time,
                'update_time' => $now_time,
                'pay_time' => $now_time,
                'status' => $status,
                'account' => 0,
                'extend' => $param['extend'],
                'env_type' => $param['env_type'],
                'channel' => $param['channel'],
            ];
            // 生成订单到数据库
            $OrdersCacheModel = new OrdersCacheModel();
            //生成订单数据
            $oid = $OrdersCacheModel->SaveData($insert);
            if ($oid === false)
            {
                throw new ApiException('下单失败,请重新操作');
            }
            //添加中间表数据
            if ($param['rid'])
            {
                $middle_res = $this->insertMiddle($param['rid'], $param['gids'], $oid);
                if ($middle_res == false)
                {
                    throw new ApiException('下单失败,请重新操作');
                }
            }
            //订单支付数量增加
            (new Orders())->inc_order_num($num, $param['order_type']);
            //商品购买人数自增,教学视频根据课程进行缓存数据
            if (in_array($param['order_type'], [6, 7, 13], true))
            {
                $OrdersCacheModel->SaveTotalPrepaidOrder($param['order_type'], $gid);
            } else
            {
                $OrdersCacheModel->SaveTotalPrepaidOrder($param['order_type'], 0);
            }

            //修改优惠券状态为已锁定
            if ($param['cid'])
            {
                $res = $CouponModel->use_return($this->user['id'], $param['cid'], ['status' => 1, 'oid' => $oid, 'update_time' => $this->request->time()]);
                if ($res === false)
                {
                    throw new ApiException($CouponModel->getError());
                }
            }

            // 删除订单缓存
            (new Orders())->rm_cache(['oid' => $oid, 'del_add' => 0, 'order_id' => $insert['order_id'], 'uid' => $insert['uid'], 'gid' => $insert['gid']]);

            //生成第三方数据和入队
            switch (true)
            {
                case $param['order_type'] == 1://校准生辰入队
                    $this->RecQueue($param['reid'], $oid, $param['out_trade_no']);
                    break;
                case $num === 2://运势入队
                    $this->FortuneQueue($this->user['id'], $oid);
                    break;
                case $param['order_type'] == 5://地图入队
                    (new PushQueue())->Map($param['out_trade_no']);
                    break;
                case $num === 5://本命预测入队
                    $this->NatalQueue($param['rid'], $param['gids'], $param['out_trade_no']);
                    break;
                case $num === 6://若是vip，更新vip有效期和生效状态
                    $middle_res = $this->pay_vip($insert, $param['goods_info']['goods']);
                    break;
            }
            //更新星力值
            if ($param['order_type'] != 8)
            {   // VIP订单除外 更新星力值，  VIP星力值已经更新过了，禁止重复更新
                OrdersLib::UpdateVipBoon($insert, $gid, true);
            }
            if ($middle_res == false)
            {
                throw new ApiException('下单失败,请重新操作');
            }

            //更新订单详情缓存
            $OrdersCacheModel->UpdateCache($param['out_trade_no'], $status);
            Db::commit();
        }
        catch (Exception $e)
        {
            Db::rollback();
            throw new ApiException('下单失败,请重新操作');
        }
        return $status;
    }

    /**
     * 校准生辰入队
     * @param $reid
     * @param $oid
     * @param $out_trade_no
     * @throws Exception
     */
    private function RecQueue($reid, $oid, $out_trade_no)
    {
        (new PushQueue())->Rectification($this->user, $reid, $oid, $out_trade_no);
    }

    /**
     * 运势入队
     * @param $uid
     * @param $oid
     * @return bool|mixed
     * @throws Exception
     */
    private function FortuneQueue($uid, $oid)
    {
        return (new PushQueue)->Fortune(['uid' => $uid, 'oid' => $oid]);
    }

    /**
     * 本命预测入队加结果数据写入
     * @param int $rid
     * @param array $gids
     * @param string $order_id
     * @throws Exception
     */
    private function NatalQueue($rid, $gids, $order_id)
    {
        $NatalReportModel = new NatalReport();
        $NatalReportModel->SaveQueue($this->user['id'], $rid, $gids, $order_id);
    }
}
